package com.base.utils;

import io.netty.util.internal.ObjectUtil;
import org.apache.commons.lang3.ObjectUtils;
import org.bytedeco.ffmpeg.avcodec.AVCodecParameters;
import org.bytedeco.ffmpeg.avformat.AVFormatContext;
import org.bytedeco.ffmpeg.avformat.AVStream;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.*;
import org.springframework.stereotype.Component;
import ws.schild.jave.*;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 此文件作为视频文件处理父类，提供：
 * 1、查看视频时长
 * 2、校验两个视频的时长是否相等
 */
public class VideoUtil {

    private static Map<String, Integer> sizeBitRateMap;

    static {
        sizeBitRateMap = new HashMap<>();
        sizeBitRateMap.put("1920*1080", 4992);
        sizeBitRateMap.put("1280*720", 2496);
        sizeBitRateMap.put("1024*576", 1856);
        sizeBitRateMap.put("840*480", 1216);
        sizeBitRateMap.put("768*432", 1088);
        sizeBitRateMap.put("640*360", 896);
        sizeBitRateMap.put("424*240", 576);
    }

    String ffmpeg_path = "D:\\常用软件安装位置\\ffmpeg-6.0-full_build\\ffmpeg-6.0-full_build\\bin\\ffmpeg.exe";//ffmpeg的安装位置

    public VideoUtil(String ffmpeg_path) {
        this.ffmpeg_path = ffmpeg_path;
    }

    public VideoUtil() {

    }


    //检查视频时间是否一致
    public Boolean check_video_time(String source, String target) {
        String source_time = get_video_time(source);
        //取出时分秒
        source_time = source_time.substring(0, source_time.lastIndexOf("."));
        String target_time = get_video_time(target);
        //取出时分秒
        target_time = target_time.substring(0, target_time.lastIndexOf("."));
        if (source_time == null || target_time == null) {
            return false;
        }
        if (source_time.equals(target_time)) {
            return true;
        }
        return false;
    }

    //获取视频时间(时：分：秒：毫秒)
    public String get_video_time(String video_path) {
        /*
        ffmpeg -i  lucene.mp4
         */
        List<String> commend = new ArrayList<String>();
        commend.add(ffmpeg_path);
        commend.add("-i");
        commend.add(video_path);
        try {
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(commend);
            //将标准输入流和错误输入流合并，通过标准输入流程读取信息
            builder.redirectErrorStream(true);
            Process p = builder.start();
            String outstring = waitFor(p);
            int start = outstring.trim().indexOf("Duration: ");
            if (start >= 0) {
                int end = outstring.trim().indexOf(", start:");
                if (end >= 0) {
                    String time = outstring.substring(start + 10, end);
                    if (time != null && !time.equals("")) {
                        return time.trim();
                    }
                }
            }

        } catch (Exception ex) {

            ex.printStackTrace();

        }
        return null;
    }

    public String waitFor(Process p) {
        InputStream in = null;
        InputStream error = null;
        String result = "error";
        int exitValue = -1;
        StringBuffer outputString = new StringBuffer();
        try {
            in = p.getInputStream();
            error = p.getErrorStream();
            boolean finished = false;
            int maxRetry = 600;//每次休眠1秒，最长执行时间10分种
            int retry = 0;
            while (!finished) {
                if (retry > maxRetry) {
                    return "error";
                }
                try {
                    while (in.available() > 0) {
                        Character c = new Character((char) in.read());
                        outputString.append(c);
                    }
                    while (error.available() > 0) {
                        Character c = new Character((char) in.read());
                        outputString.append(c);
                    }
                    //进程未结束时调用exitValue将抛出异常
                    exitValue = p.exitValue();
                    finished = true;

                } catch (IllegalThreadStateException e) {
                    Thread.currentThread().sleep(1000);//休眠1秒
                    retry++;
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return outputString.toString();

    }


    /**
     * 删除临时文件
     *
     * @param files
     */
    public void deleteFile(File... files) {
        for (File file : files) {
            if (file.exists()) {
                file.delete();
            }
        }
    }


    /**
     * @param source     源文件
     * @param targetPath 转码后的路径
     */
    public void convertVideoToMP4(File source, String targetPath) {
        MultimediaObject multimediaObject = new MultimediaObject(source);
        try {
            MultimediaInfo info = multimediaObject.getInfo();
            VideoInfo videoInfo = info.getVideo();
            VideoSize size = videoInfo.getSize();
            System.out.println("原视频宽：" + size.getWidth());
            System.out.println("原视频高：" + size.getHeight());
            System.out.println("原视频比特率：" + videoInfo.getBitRate() / 1000);
            System.out.println("原视频编码：" + videoInfo.getDecoder());

            Integer bitRate = sizeBitRateMap.get(size.getWidth() + "*" + size.getHeight());
            VideoAttributes video = new VideoAttributes();
            //设置视频编码
            video.setCodec("h264");

            if (ObjectUtils.isNotEmpty(bitRate)) {
                //设置比特率
                video.setBitRate(bitRate * 1000);
            }
            File target = new File(targetPath);
            AudioAttributes audio = new AudioAttributes();
            //设置编码器名称
            audio.setCodec("aac");
            EncodingAttributes attrs = new EncodingAttributes();
            //设置转换后的格式
            attrs.setFormat("mp4");
            attrs.setAudioAttributes(audio);
            attrs.setVideoAttributes(video);
            Encoder encoder = new Encoder();
            encoder.encode(multimediaObject, target, attrs);
            //花费毫秒数

            MultimediaObject multimediaObjectOfter = new MultimediaObject(Paths.get(targetPath).toFile());
            MultimediaInfo info1 = multimediaObjectOfter.getInfo();
            VideoInfo video1 = info1.getVideo();
            VideoSize size1 = video1.getSize();

            System.out.println("转换后视频宽：" + size1.getWidth());
            System.out.println("转换后视频高：" + size1.getHeight());
            System.out.println("转换后视频比特率：" + video1.getBitRate() / 1000);
            System.out.println("转换后视频编码：" + video1.getDecoder());

        } catch (EncoderException e) {
            e.printStackTrace();
        }
    }

    /**
     * 视频转码函数(仅转码)
     *
     * @param inputfile  原始视频文件完整路径
     * @param outputfile 目标视频文件完整保存路径（必须完整文件名，即包含格式后缀，推荐格式后缀为.mp4）
     * @throws Exception 异常
     */
    public  void videoConvert(String inputfile, String outputfile) throws Exception
    {
        if (outputfile.lastIndexOf('.') < 0)
        {
            throw new Exception("Error! Output file format undetected!");
        }
        String format = outputfile.substring(outputfile.lastIndexOf('.'));

        FFmpegLogCallback.set();
        Frame frame;
        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputfile);
        FFmpegFrameRecorder recorder = null;

        try
        {
            System.out.println("开始初始化帧抓取器");

            // 初始化帧抓取器，例如数据结构（时间戳、编码器上下文、帧对象等），
            // 如果入参等于true，还会调用avformat_find_stream_info方法获取流的信息，放入AVFormatContext类型的成员变量oc中
            grabber.start(true);

            System.out.println("帧抓取器初始化完成");

            // grabber.start方法中，初始化的解码器信息存在放在grabber的成员变量oc中
            AVFormatContext avformatcontext = grabber.getFormatContext();

            // 文件内有几个媒体流（一般是视频流+音频流）
            int streamNum = avformatcontext.nb_streams();

            // 没有媒体流就不用继续了
            if (streamNum < 1)
            {
                System.out.println("文件内不存在媒体流");
                throw new Exception("Error! There is no media stream in the file!");
            }

            // 取得视频的帧率
            double framerate = grabber.getVideoFrameRate();

            System.out.printf("视频帧率[%f]，视频时长[%d]秒，媒体流数量[%d]\r\n", framerate, avformatcontext.duration() / 1000000,
                    avformatcontext.nb_streams());

            // 遍历每一个流，检查其类型
            for (int i = 0; i < streamNum; i++)
            {
                AVStream avstream = avformatcontext.streams(i);
                AVCodecParameters avcodecparameters = avstream.codecpar();
                System.out.printf("流的索引[%d]，编码器类型[%d]，编码器ID[%d]\r\n", i, avcodecparameters.codec_type(),
                        avcodecparameters.codec_id());
            }

            // 视频宽度
            int frameWidth = grabber.getImageWidth();
            // 视频高度
            int frameHeight = grabber.getImageHeight();
            // 音频通道数量
            int audiochannels = grabber.getAudioChannels();

            System.out.printf("视频宽度[%d]，视频高度[%d]，音频通道数[%d]\r\n", frameWidth, frameHeight, audiochannels);

            recorder = new FFmpegFrameRecorder(outputfile, frameWidth, frameHeight, audiochannels);
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);

            recorder.setFormat(format);
            // 使用原始视频的码率，若需要则自行修改码率
            recorder.setVideoBitrate(grabber.getVideoBitrate());
            //将视频码率修改为1024
//            recorder.setVideoBitrate(2327000);

            // 一秒内的帧数，帧率
            recorder.setFrameRate(framerate);

            // 两个关键帧之间的帧数
            recorder.setGopSize((int)framerate);

            // 设置音频通道数，与视频源的通道数相等
            recorder.setAudioChannels(grabber.getAudioChannels());

            recorder.start();

            int videoframenum = 0;
            int audioframenum = 0;
            int dataframenum = 0;

            // 持续从视频源取帧
            while (null != (frame = grabber.grab()))
            {
                // 有图像，就把视频帧加一
                if (null != frame.image)
                {
                    videoframenum++;
                    // 取出的每一帧，都保存到视频
                    recorder.record(frame);
                }

                // 有声音，就把音频帧加一
                if (null != frame.samples)
                {
                    audioframenum++;
                    // 取出的每一帧，都保存到视频
                    recorder.record(frame);
                }

                // 有数据，就把数据帧加一
                if (null != frame.data)
                {
                    dataframenum++;
                }
            }

            System.out.printf("转码完成，视频帧[%d]，音频帧[%d]，数据帧[%d]\r\n", videoframenum, audioframenum, dataframenum);

        } catch (Exception e)
        {
            // e.printStackTrace();
            throw e;
        } finally
        {
            if (recorder != null)
            {
                try
                {
                    recorder.close();
                } catch (Exception e)
                {
                    // System.out.println("recorder.close异常" + e);
                    throw e;
                }
            }

            try
            {
                grabber.close();
            } catch (FrameGrabber.Exception e)
            {
                // System.out.println("frameGrabber.close异常" + e);
                throw e;
            }
        }
    }

}
